/*
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2006-2007 Intel Corporation. All Rights Reserved.
//
*/
#include "umc_defs.h"
#if defined (UMC_ENABLE_DV_VIDEO_ENCODER)

#include "umc_dv_enc_compressor_def.h"
#include "umc_dv_enc_encode_bit_stream.h"
#include "umc_dv_enc_huffman.h"

#include "umc_dv_enc_block.h"
#include "umc_dv_enc_segment_compressor.h"

namespace UMC
{

#define ELEMENTh(run, cod, dl) (cod<< 16) | dl
#define ELEMENThEnd(run, cod, dl) ((cod + run)<< 16) | dl

static Ipp32u EncodeTablesIntern[64] = {0, ELEMENTh(0, 0x07ce, 11),
      ELEMENTh(1, 0x07cf, 11), ELEMENTh(2, 0x0fac, 12), ELEMENTh(3, 0x0fad, 12),
      ELEMENTh(4, 0x0fae, 12), ELEMENTh(5, 0x0faf, 12), ELEMENTh(6, 0x1f86, 13),

      ELEMENThEnd(7, 0x1f80, 13), ELEMENThEnd(8, 0x1f80, 13), ELEMENThEnd(9, 0x1f80, 13),

      ELEMENThEnd(10, 0x1f80, 13), ELEMENThEnd(11, 0x1f80, 13), ELEMENThEnd(12, 0x1f80, 13),
      ELEMENThEnd(13, 0x1f80, 13), ELEMENThEnd(14, 0x1f80, 13), ELEMENThEnd(15, 0x1f80, 13),
      ELEMENThEnd(16, 0x1f80, 13), ELEMENThEnd(17, 0x1f80, 13), ELEMENThEnd(18, 0x1f80, 13),
      ELEMENThEnd(19, 0x1f80, 13), ELEMENThEnd(20, 0x1f80, 13), ELEMENThEnd(21, 0x1f80, 13),
      ELEMENThEnd(22, 0x1f80, 13), ELEMENThEnd(23, 0x1f80, 13), ELEMENThEnd(24, 0x1f80, 13),
      ELEMENThEnd(25, 0x1f80, 13), ELEMENThEnd(26, 0x1f80, 13), ELEMENThEnd(27, 0x1f80, 13),
      ELEMENThEnd(28, 0x1f80, 13), ELEMENThEnd(29, 0x1f80, 13), ELEMENThEnd(30, 0x1f80, 13),
      ELEMENThEnd(31, 0x1f80, 13), ELEMENThEnd(32, 0x1f80, 13), ELEMENThEnd(33, 0x1f80, 13),
      ELEMENThEnd(34, 0x1f80, 13), ELEMENThEnd(35, 0x1f80, 13),/////////////////////////////////, ELEMENThEnd(36, 0x1f80, 13),

      ELEMENThEnd(36, 0x1f80, 13), ELEMENThEnd(37, 0x1f80, 13), ELEMENThEnd(38, 0x1f80, 13),
      ELEMENThEnd(39, 0x1f80, 13), ELEMENThEnd(40, 0x1f80, 13), ELEMENThEnd(41, 0x1f80, 13),
      ELEMENThEnd(42, 0x1f80, 13), ELEMENThEnd(43, 0x1f80, 13), ELEMENThEnd(44, 0x1f80, 13),
      ELEMENThEnd(45, 0x1f80, 13), ELEMENThEnd(46, 0x1f80, 13), ELEMENThEnd(47, 0x1f80, 13),
      ELEMENThEnd(48, 0x1f80, 13), ELEMENThEnd(49, 0x1f80, 13), ELEMENThEnd(50, 0x1f80, 13),
      ELEMENThEnd(51, 0x1f80, 13), ELEMENThEnd(52, 0x1f80, 13),////////////////////////////////, ELEMENThEnd(53, 0x1f80, 13),
      ELEMENThEnd(53, 0x1f80, 13), ELEMENThEnd(54, 0x1f80, 13), ELEMENThEnd(55, 0x1f80, 13),
      ELEMENThEnd(56, 0x1f80, 13), ELEMENThEnd(57, 0x1f80, 13), ELEMENThEnd(58, 0x1f80, 13),
      ELEMENThEnd(59, 0x1f80, 13), ELEMENThEnd(60, 0x1f80, 13), ELEMENThEnd(61, 0x1f80, 13),
      ELEMENThEnd(62, 0x1f80, 13)
     };

static Ipp32s LastElemenInAreaNum[] ={ 6<<8, 21<<8, 43<<8, 64<<8 };

void SegmentCompressor::QuantizeBlock(BLOCK *lpBlock, Ipp32u qno)
{
    Ipp32u QuantStep;
    Ipp32s curr_elem, run_len, quantized_amp, area_num, last_elem_in_area, signX;
    Ipp32u *lpsSrc = lpBlock->m_lpsDataRL;
    Ipp32u *lpsDest = lpBlock->m_lpsDataRL + 1;

    QuantStep = TABLE_QS[qno * 4 + lpBlock->m_cC1C0].qu_step | 0x10101010;

    // first element is never quantized
    curr_elem = *(++lpsSrc);
    run_len = 0;
    for(area_num = 0; area_num < 4; area_num++)
    {
        last_elem_in_area = LastElemenInAreaNum[area_num];
        while( (curr_elem & 0x7f00) < last_elem_in_area)
        {
            run_len += (curr_elem & 0xff);
            quantized_amp = (curr_elem >> (QuantStep & 0xff));
            if(quantized_amp != 0)
            {
                signX = (curr_elem & 0x8000) << 1;

                // code as single element
                if (quantized_amp <= TableMaxAmpOnRun[run_len])
                    *lpsDest++ = ((EncodeTables[run_len][quantized_amp]).code << 16) | signX | ((EncodeTables[run_len][quantized_amp]).length);
                else // code as 2 element
                    *lpsDest++ = ((EncodeTables[0][quantized_amp]).code << 16) | signX | ((EncodeTables[0][quantized_amp]).length) | (run_len << 8);

                run_len = 0;
            }
            else run_len++;
            curr_elem = *(++lpsSrc);
        }
        QuantStep >>=8;
    }

    *lpsDest = 0x4000;
}

Ipp32s GetEncodedBlockSize(BLOCK *lpBlock, Ipp32u qno)
{
    Ipp32u QuantStep;
    Ipp32s EncodedBlockSize = 16;// DC + m0 + c1c0 + eob
    Ipp32s curr_elem, run_len, quantized_amp, area_num, last_elem_in_area;
    Ipp32u *lpsSrc = lpBlock->m_lpsDataRL;

    QuantStep = TABLE_QS[qno * 4 + lpBlock->m_cC1C0].qu_step | 0x10101010;

    // first element is never prequantized
    curr_elem = *(++lpsSrc);
    run_len = 0;
    for(area_num = 0; area_num < 4; area_num++)
    {
        last_elem_in_area = LastElemenInAreaNum[area_num];
        while( (curr_elem & 0x7f00) < last_elem_in_area)
        {
            run_len += (curr_elem & 0xff);
            quantized_amp = (curr_elem >> (QuantStep & 0xff));
            if(quantized_amp != 0)
            {
                // code as single element
                if (quantized_amp <= TableMaxAmpOnRun[run_len])
                    EncodedBlockSize += (EncodeTables[run_len][quantized_amp]).length;
                else // code as 2 element
                {
                    EncodedBlockSize += (EncodeTables[run_len - 1][0x00]).length;
                    EncodedBlockSize += (EncodeTables[0x00][quantized_amp]).length;
                }
                run_len = 0;
            }
            else run_len++;
            curr_elem = *(++lpsSrc);
        }
        QuantStep >>=8;
    }
    return EncodedBlockSize;
}



//inline
Ipp32u ByteSwap(Ipp32u i)
{
    //return ((i << 24) | ((i & 0x0000ff00) << 8) | ((i & 0x00ff0000) >> 8) | (i >> 24));
    //Ipp32u testVal = ((i << 24) | ((i & 0x0000ff00) << 8) | ((i & 0x00ff0000) >> 8) | (i >> 24));
    Ipp8u *tempByte =(Ipp8u *) &i;
    Ipp8u temp = tempByte[0];
    tempByte[0] = tempByte[3];
    tempByte[3] = temp;
    temp = tempByte[1];
    tempByte[1] = tempByte[2];
    tempByte[2] = temp;
    return i;
} // Ipp32u ByteSwap(Ipp32u i)

Ipp32u ByteSwap(Ipp8u *currPointer)
{
    Ipp32u byteSwapValue;
    Ipp8u *tempByte =(Ipp8u *) &byteSwapValue;

    tempByte[0] = currPointer[3];
    tempByte[1] = currPointer[2];
    tempByte[2] = currPointer[1];
    tempByte[3] = currPointer[0];

    return byteSwapValue;
} // Ipp32u ByteSwap(Ipp32u i)


Ipp32s EncodeBitStream(ENCODE_BIT_STREAM *lpStream, BLOCK *lpBlock)
{
    Ipp64u Reg = 0;
    Ipp32s iFreeBits = 64 - 9 - 3;//free bits in register. Total 64 bits, 12 already used for DC, m1, c1,c0
    Ipp32s iSize = 0 + 9 + 3;// 9 bits for DC, 3 bits for c1 c0 and m0
    Ipp32s temp;
    Ipp32u *lpsSrc = lpBlock->m_lpsDataRL;
    Ipp32s *lpiDst = (Ipp32s *) lpStream->m_lpcDest;
    Ipp32s iToReturn;

#ifdef _DEBUG
    memset(lpiDst, 0, 128);
#endif //_DEBUG

    // encode DC
    Reg = ((Ipp64u) (lpsSrc[0] & 0x01ff)) << (64 - 9);
    // encode m0 & c1c0
    Reg |= ((Ipp64u) ((lpBlock->m_cM0 << 2) | (lpBlock->m_cC1C0))) << (64 - 9 - 3);

    temp = *(++lpsSrc);
    while (!(temp & 0x4000))
    {
        Ipp32s code_len;
        code_len = temp & 0xff;
//      temp1 += (temp & 0xff);
        if (!(temp  & 0xff00))
        {// code as single element
            iSize += code_len;
            iFreeBits -= code_len;
            Reg |= ((Ipp64u) (temp & 0xffff0000)) << (iFreeBits - 16);
        }
        else // code as 2 element
        {
            Ipp32u RunXAmp0 = (Ipp32u)EncodeTablesIntern[(temp & 0x7f00) >> 8];
            Reg |= (( (Ipp64u) ( (RunXAmp0 & 0xffff0000) | ((temp & 0xffff0000) >> code_len) ) )
                << (iFreeBits - 16 - (RunXAmp0 & 0xffff)) );
            code_len += (RunXAmp0 & 0xffff);
            iSize += code_len;
            iFreeBits -= code_len;
        }
        if (iFreeBits < 32)
        {
            iFreeBits += 32;
            *lpiDst = ByteSwap((Ipp32s) (Reg >> 32));
            lpiDst++;
            Reg <<= 32;
        }
        temp = *(++lpsSrc);
    }
    // set eob
    iSize += 4;
    iFreeBits -= 4;
    Reg |= ((Ipp64s) (0x06)) << iFreeBits;

    if (iFreeBits < 32)
    {
        iFreeBits += 32;
        *lpiDst = ByteSwap((Ipp32s) (Reg >> 32));
        lpiDst++;
        Reg <<= 32;
    }

    *lpiDst = ByteSwap((Ipp32s) (Reg >> 32));

    if (iSize < lpStream->m_bBitsCanTake)
    {
        lpStream->m_lpcDest = (Ipp8u *) lpiDst;
        lpStream->m_bFreeBits = iFreeBits;
        lpStream->m_bBitsCanTake -= iSize;
        lpStream->m_wExtraBits = 0;
        lpStream->m_dwRegister = ((Ipp32s) (Reg >> 32));

        iToReturn = STREAM_NOT_FULL;
    }
    else if (iSize > lpStream->m_bBitsCanTake)
    {
        lpStream->m_lpcDest += lpStream->m_bBitsCanTake / 8;
        lpStream->m_bFreeBits = 64;
        lpStream->m_wExtraBits = iSize - lpStream->m_bBitsCanTake;
        lpStream->m_bBitsCanTake = 0;
        lpStream->m_dwRegister = 0;

        iToReturn = STREAM_OVER;
    }
    else // if (iSize == m_bBitsCanTake)
    {
        iToReturn = STREAM_FULL;
    }

    return iToReturn;

} // Ipp32s EncodeBitStream(ENCODE_BIT_STREAM *lpStream, BLOCK *lpBlock)

Ipp32s AddBits(ENCODE_BIT_STREAM *lpStreamDst, ENCODE_BIT_STREAM *lpStreamSrc)
{
    Ipp64u RegIn, RegOut;
    Ipp32s iFreeIn, iFreeOut;
    Ipp32s iToReturn;
    Ipp32s iBits;

    RegIn = ((Ipp64u) lpStreamSrc->m_dwRegister) << 32;
    RegOut = ((Ipp64u) lpStreamDst->m_dwRegister) << 32;
    iFreeIn = lpStreamSrc->m_bFreeBits;
    iFreeOut = lpStreamDst->m_bFreeBits;

    if (lpStreamDst->m_bBitsCanTake > lpStreamSrc->m_wExtraBits)
    {
        iBits = lpStreamSrc->m_wExtraBits;
        iToReturn = STREAM_NOT_FULL;

        lpStreamDst->m_bBitsCanTake -= lpStreamSrc->m_wExtraBits;
    }
    else // if(m_bBitsCanTake <= lpStream->m_wExtraBits)
    {
        iBits = lpStreamDst->m_bBitsCanTake;
        iToReturn = STREAM_FULL;

        lpStreamSrc->m_wExtraBits -= lpStreamDst->m_bBitsCanTake;
    }

    while (iBits >= 32)
    {
        //Ipp32s temp
        //RegIn |= ((Ipp64u) ByteSwap(*((Ipp32s *) lpStreamSrc->m_lpcDest))) << (iFreeIn - 32);
        RegIn |= ((Ipp64u) ByteSwap( lpStreamSrc->m_lpcDest)) << (iFreeIn - 32);
        RegOut |= RegIn >> (64 - iFreeOut);
        lpStreamSrc->m_lpcDest += 4;

        RegIn <<= 32;
        *((Ipp32s *) lpStreamDst->m_lpcDest) = ByteSwap((Ipp32s) (RegOut >> 32));
        RegOut <<= 32;
        lpStreamDst->m_lpcDest += 4;

        iBits -= 32;
    }

    //RegIn |= ((Ipp64u) ByteSwap(*((Ipp32s *) lpStreamSrc->m_lpcDest))) << (iFreeIn - 32);
    RegIn |= ((Ipp64u) ByteSwap( lpStreamSrc->m_lpcDest)) << (iFreeIn - 32);
    RegOut |= RegIn >> (64 - iFreeOut);
    lpStreamSrc->m_lpcDest += 4;

    iFreeIn -= (32 - iBits);
    RegIn <<= iBits;
    iFreeOut -= iBits;
    RegOut &= ( (Ipp64s(-1)) << iFreeOut);

    if (iFreeOut < 32)
    {
        iFreeOut += 32;
        *((Ipp32s *) lpStreamDst->m_lpcDest) = ByteSwap((Ipp32s) (RegOut >> 32));
        lpStreamDst->m_lpcDest += 4;
        RegOut <<= 32;
    }

    if (iFreeIn < 32)
    {
        iFreeIn += 32;
        lpStreamSrc->m_lpcDest -= 4;
    }

    // store data
    lpStreamSrc->m_dwRegister = (Ipp32s) (RegIn >> 32);
    lpStreamSrc->m_bFreeBits = iFreeIn;

    lpStreamDst->m_dwRegister = (Ipp32s) (RegOut >> 32);
    lpStreamDst->m_bFreeBits = iFreeOut;

    // flash down
    *((Ipp32s *) lpStreamDst->m_lpcDest) = ByteSwap((Ipp32s) (RegOut >> 32));

    return iToReturn;

} // Ipp32s AddBits(ENCODE_BIT_STREAM *lpStreamDst, ENCODE_BIT_STREAM *lpStreamSrc)

}//namespace UMC

#endif //(UMC_ENABLE_DV_VIDEO_ENCODER)
